package com.myzl.stream.service.impl;

import com.myzl.stream.service.IMediaVideoTransferService;
import com.myzl.stream.util.MediaVideoUtils;
import lombok.extern.slf4j.Slf4j;
import org.bytedeco.ffmpeg.global.avcodec;
import org.bytedeco.ffmpeg.global.avutil;
import org.bytedeco.javacv.*;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;

/**
 * @author Administrator
 */
@Slf4j
@Service
public class MediaVideoTransferServiceImpl implements IMediaVideoTransferService {
    /**
     * 开启获取rtsp流
     */
    @Override
    @Async("threadPoolTaskExecutor")
    public void live(Integer key,String rtsp,String rtmp) {
        log.info("连接rtsp：" + rtsp + ",开始创建grabber");
        createGrabber(key,rtsp,rtmp);
    }

    /**
     * 构造视频抓取器
     *
     * @param key 标识
     * @param rtsp 拉流地址
     * @param rtmp 拉流地址
     */
    private void createGrabber(Integer key,String rtsp,String rtmp) {
        // 帧抓取器(FrameGrabbe)
        FFmpegFrameGrabber grabber = null;
        // 帧录制器/推流器(FrameRecorder)
        FFmpegFrameRecorder recorder = null;
        // 获取视频源
        try {
            avutil.av_log_set_level(avutil.AV_LOG_ERROR);
            FFmpegLogCallback.set();
            grabber = FFmpegFrameGrabber.createDefault(rtsp);
            grabber.setOption("rtsp_transport", "tcp");
            // 设置采集器构造超时时间
            grabber.setOption("stimeout", "2000000");
            grabber.start();
            double framerate = 25;
            // 部分监控设备流信息里携带的帧率为9000，如出现此问题，会导致dts、pts时间戳计算失败，播放器无法播放，故出现错误的帧率时，默认为25帧
            if (grabber.getFrameRate() > 0 && grabber.getFrameRate() < 100) {
                framerate = grabber.getFrameRate();
            }

            recorder = new FFmpegFrameRecorder(rtmp, grabber.getImageWidth(), grabber.getImageHeight(), grabber.getAudioChannels());
            recorder.setInterleaved(true);
            // 降低编码延时
            recorder.setVideoOption("tune", "zerolatency");
            // 提升编码速度
            recorder.setVideoOption("preset", "ultrafast");
            // 视频质量参数(详见 https://trac.ffmpeg.org/wiki/Encode/H.264)
            recorder.setVideoOption("crf", "28");
            // 分辨率
            recorder.setVideoBitrate(grabber.getVideoBitrate());
            // 视频编码格式
            recorder.setVideoCodec(avcodec.AV_CODEC_ID_H264);
            recorder.setPixelFormat(avutil.AV_PIX_FMT_YUV420P);
            // 视频帧率
            recorder.setFrameRate(framerate);
            recorder.setGopSize((int) framerate*2 );
            //转码格式
            recorder.setFormat("flv");
            //采样率
            recorder.setSampleRate(grabber.getSampleRate());

            recorder.setAudioOption("crf", "0");
            recorder.setAudioQuality(0);
            recorder.setAudioBitrate(192000);

            // 建议从grabber获取AudioChannels
            recorder.setAudioChannels(grabber.getAudioChannels());
            recorder.setAudioCodec(avcodec.AV_CODEC_ID_AAC);

            startPushStream(key,rtsp,rtmp,grabber,recorder);
        } catch (FrameGrabber.Exception e) {
            log.error("创建解析FFmpegFrameGrabber失败:", e);
            MediaVideoUtils.removeConnect(key);
            this.stop(grabber,recorder);
            this.reset(grabber,recorder);
        }
    }

    /**
     * 推送流
     * @param key 标识
     * @param rtsp 拉流地址
     * @param rtmp 拉流地址
     * @param grabber 帧抓取器(FFmpegFrameGrabber)
     * @param recorder 帧录制器/推流器(FrameRecorder)
     */

    private void startPushStream(Integer key,String rtsp,String rtmp,FFmpegFrameGrabber grabber,FFmpegFrameRecorder recorder) {
        if (grabber == null) {
            log.info("重试连接src：" + rtsp + ",开始创建grabber");
            createGrabber(key,rtsp,rtmp);
        }
        try {
            recorder.start();
            log.info("start recoder");
            grabber.flush();
            Frame capturedFrame;

            int oldFrameNumber = Integer.MIN_VALUE;
            long oldTimestamp = Long.MIN_VALUE;
            int noFrameIndex = 0;
            while ((capturedFrame = grabber.grab()) != null && noFrameIndex < 5) {
               //增加一个判断跳出循环
                if (!MediaVideoUtils.exists(key)) {
                    break;
                }
                if (ObjectUtils.isEmpty(capturedFrame)){
                    noFrameIndex++;
                    continue;
                }
                int newFrameNumber = grabber.getFrameNumber();
                long newTimestamp = capturedFrame.timestamp;
                if (newFrameNumber < oldFrameNumber) {
                    continue;
                } else if (newFrameNumber > oldFrameNumber) {
                    oldTimestamp = Long.MIN_VALUE;
                }
                if (newTimestamp < oldTimestamp) {
                    continue;
                }
                oldFrameNumber = newFrameNumber;
                oldTimestamp = newTimestamp;
                recorder.setTimestamp(capturedFrame.timestamp);
                recorder.record(capturedFrame);
            }

            //增加一个判断断流重连
            if (MediaVideoUtils.exists(key)) {
                log.info("重试连接：" + rtsp);
                this.stop(grabber,recorder);
                createGrabber(key,rtsp,rtmp);
            } else {
                this.stop(grabber,recorder);
                this.reset(grabber,recorder);
            }
        } catch (FrameGrabber.Exception | RuntimeException | FrameRecorder.Exception e) {
            log.error("exception", e);
            //增加一个判断断流重连
            /*if (MediaVideoUtils.exists(key)) {
                log.info("重试连接：" + rtsp);
                this.stop(grabber,recorder);
                createGrabber(key,rtsp,rtmp);
            } else {
                this.stop(grabber,recorder);
                this.reset(grabber,recorder);
            }*/
            MediaVideoUtils.removeConnect(key);
            this.stop(grabber,recorder);
            this.reset(grabber,recorder);
        }
    }

    /**
     * 停止
     * @param grabber 帧抓取器(FFmpegFrameGrabber)
     * @param recorder 帧录制器/推流器(FrameRecorder)
     */
    private void stop(FFmpegFrameGrabber grabber,FFmpegFrameRecorder recorder) {
        try {

            if (grabber != null) {
                grabber.stop();
                grabber.close();
            }

            if (recorder != null) {
                recorder.stop();
                recorder.release();
            }
        } catch (Exception e) {
            log.error("stop exception:", e);
        }
    }

    /**
     * 重置
     * @param grabber 帧抓取器(FFmpegFrameGrabber)
     * @param recorder 帧录制器/推流器(FrameRecorder)
     */
    private void reset(FFmpegFrameGrabber grabber,FFmpegFrameRecorder recorder) {
        recorder = null;
        grabber = null;
    }


}


